#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "ESPacketBase.h"
using namespace std;

extern "C"  
{  
    #include "lua.h"  
    #include "lauxlib.h"  
    #include "lualib.h"  
}  

#if defined(_WIN32) || defined(_WIN64)
#define _DLLExport _declspec(dllexport)
#else
#define _DLLExport 
#endif

static int lua_create_writebase(lua_State* L)
{
    NETOutputPacket** p = (NETOutputPacket**)lua_newuserdata(L, sizeof(NETInputPacket));
    *p = new NETOutputPacket;
    
    luaL_getmetatable(L, "espacketbase");
    lua_setmetatable(L, -2);

    return 1;
}

static int lua_create_readbase(lua_State* L)
{
    NETInputPacket** p = (NETInputPacket**)lua_newuserdata(L, sizeof(NETInputPacket));
    *p = new NETInputPacket;

    luaL_getmetatable(L, "espacketbase");
    lua_setmetatable(L, -2);

    return 1;
}

static int WriteBegin(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    short nCommand = luaL_checkinteger(L, 2);

    //默认值
    char cVersion = 1;
    
    int ver_flag = lua_isinteger(L, 3);
    if (ver_flag == 1)
    {
        cVersion = luaL_checkinteger(L, 3);
    }

    //printf("cVersion=%d\n", cVersion);
    //printf("cSubVersion=%d\n", cSubVersion);

    (*p)->Begin(nCommand, cVersion);

    return 1;
}

static int WriteEnd(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    (*p)->End();
    // (*p)->EncryptBuffer();

    return 1;
}

// static int EncryptBuffer(lua_State *L)
// {
//     NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
//     luaL_argcheck(L, p != NULL, 1, "invalid user data");

//     (*p)->EncryptBuffer();

//     return 1;
// }

static int DecryptBuffer(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    // int encrypt_size = (*p)->DecryptBuffer();
    int encrypt_size = (*p)->packet_size(); // IPK ES 包没有加密解密, 默认返回包大小
    lua_pushinteger(L, encrypt_size);

    return 1;
}

static int Copy(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    void* msg = lua_touserdata(L, 2);
    luaL_argcheck(L, msg != NULL, 1, "invalid msg data");

    int len = (int)luaL_checkinteger(L, 3);

    int encrypt_size = (*p)->Copy(msg, len);

    lua_pushinteger(L, encrypt_size);

    return 1;
}

static int GetCmdType(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    short nCmdType = (*p)->GetCmdType();
    lua_pushinteger(L, nCmdType);

    return 1;
}

static int return_buf(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    int size = (*p)->packet_size();
    char *v = (*p)->return_buf();

    lua_pushlightuserdata(L, v);
    lua_pushinteger(L, size);
    //free(v);
    
    return 2;
}

static int WriteInt(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    int nValue = (int)luaL_checkinteger(L, -1);
    bool ret = (*p)->WriteInt(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteString(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");
    
    const char *pString = luaL_checkstring(L, -1);
    
    bool ret = (*p)->WriteString(pString);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteShort(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    short nValue = (short)luaL_checkinteger(L, -1);
    bool ret = (*p)->WriteShort(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteInt64(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    long long nValue = luaL_checknumber(L, -1);

    bool ret = (*p)->WriteInt64(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteFloat64(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    double nValue = luaL_checknumber(L, -1);

    // bool ret = (*p)->WriteFloat64(nValue);
    bool ret = (*p)->WriteInt64(nValue); // IPK 不支持, 默认返回全部

    lua_pushinteger(L, ret);

    return 1;
}

static int WriteByte(lua_State *L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    unsigned char nValue = luaL_checkinteger(L, -1);
    bool ret = (*p)->WriteByte(nValue);

    lua_pushinteger(L, ret);

    return 1;
}

static int ReadInt(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    int v = (*p)->ReadInt();

    lua_pushinteger(L, v);

    return 1;
}

static int ReadShort(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    short v = (*p)->ReadShort();
    // 这里对short类型进行特殊的处理
    int vv = 0x0000FFFF&v;
    lua_pushinteger(L, vv);
    return 1;
}

static int ReadByte(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    char v = (*p)->ReadByte();
    lua_pushinteger(L, v);
    return 1;
}

static int ReadInt64(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    long long v = (*p)->ReadInt64();

    //string s = (char*)((*p)->ReadInt64());
    //lua_pushstring(L, s.c_str());

    lua_pushinteger(L, v);
    
    return 1;
}

static int ReadString(lua_State *L)
{
    NETInputPacket** p = (NETInputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");

    string v = (*p)->ReadString();
    lua_pushstring(L, v.c_str());

    return 1;
}

static int lua_delete_base(lua_State* L)
{
    NETOutputPacket** p = (NETOutputPacket**)luaL_checkudata(L, 1, "espacketbase");
    luaL_argcheck(L, p != NULL, 1, "invalid user data");
    
    if (p) {
        //std::cout<<"lua_delete_base!"<<endl;
        delete *p;
    }

    return 1;
}

static const luaL_Reg registerInfo[] = {
    {"__gc", lua_delete_base},
   
    {"create_writebase", lua_create_writebase},
    {"create_readbase", lua_create_readbase},
    {"read_int", ReadInt},
    {"read_short", ReadShort},
    {"read_byte", ReadByte},
    {"read_int64", ReadInt64},
    {"read_string", ReadString},

    {"write_begin", WriteBegin},
    {"write_end", WriteEnd},
    {"write_int", WriteInt},
    {"write_int64", WriteInt64},
    {"write_float64", WriteFloat64},
    {"write_byte", WriteByte},
    {"write_string", WriteString},
    {"write_short", WriteShort},

    //{"encrypt_buffer", EncryptBuffer},
    {"return_buf", return_buf},

    {"decrypt_buffer", DecryptBuffer},
    {"copy", Copy},
    {"get_cmd_type", GetCmdType},

    {NULL, NULL}
};


extern "C" _DLLExport int luaopen_lua_espacketbase(lua_State *L)
{
    luaL_newmetatable(L, "espacketbase");
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");

	luaL_setfuncs(L, registerInfo, 0);

    return 1;
}
